import { _decorator, Component, instantiate, Node, NodePool, sp, tween, UITransform, v2, v3, Vec3 } from 'cc';
import { EFloat, FloatIns, GetFloatComponent, GetFloatPrefab } from '../float/float.config';
const { ccclass, property } = _decorator;

@ccclass('Float')
export class Float extends Component {
	private static _ins: Float;
	/** 全局唯一安全实例 */
	public static get ins(){
		return Float._ins;
	}

	private xx_pool: NodePool;
	private xx_demo: Node;
	private uitrans: UITransform;

	protected onLoad(): void {
		Float._ins = this;
		this.uitrans = this.node.getComponent(UITransform);
		this.xx_demo = this.node.getChildByName('xingxing');
		this.xx_pool = new NodePool();
		this.xx_pool.put(this.xx_demo);
	}

	private static float_pool: Map<any, NodePool> = new Map();

	private static async getFloat<F extends EFloat>(f: F): Promise<FloatIns<F>>{
		let pool = Float.float_pool.get(f);
		if(!pool){
			pool = new NodePool();
			Float.float_pool.set(f, pool);
		}

		let node: Node;
		if(pool.size() > 0) node = pool.get();
		else{
			node = instantiate(await GetFloatPrefab(f));
		}

		//@ts-ignore
		return node.getComponent(await GetFloatComponent(f));
	}

	public static recycleFlot(sign: any, node: Node){
		let pool = Float.float_pool.get(sign);
		if(!pool){
			pool = new NodePool();
			Float.float_pool.set(sign, pool);
		}

		pool.put(node);
	}

	public static async Hint(str: string){
		let comp = await this.getFloat(EFloat.HINT);
		comp.node.setParent(Float.ins.node);

		Float.recycleFlot(EFloat.HINT, await comp.show(str));
	}

	public static async FruitDiamond(){
		let comp = await this.getFloat(EFloat.FRUIT_DIAMOND);
		comp.node.setParent(Float.ins.node);

		Float.recycleFlot(EFloat.FRUIT_DIAMOND, await comp.show(''));
	}

	public static async FruitSpecial(){
		let comp = await this.getFloat(EFloat.FRUIT_SPECIAL);
		comp.node.setParent(Float.ins.node);

		Float.recycleFlot(EFloat.FRUIT_SPECIAL, await comp.show(''));
	}

	private static prop_pool: Map<Node, NodePool> = new Map();
	private static prop_list: Array<{type: 'stash'|'revoke'|'shuffle', icon: Node, node: Node}> = [];
	public static addProp(type: 'stash' | 'revoke' | 'shuffle', icon: Node){
		let pool = this.prop_pool.get(icon);
		if(!pool){
			pool = new NodePool();
			this.prop_pool.set(icon, pool);
		}

		let node = pool.get() ?? instantiate(icon);
		node.setParent(Float.ins.node);
		node.setPosition(Float.ins.uitrans.convertToNodeSpaceAR(icon.worldPosition));

		this.prop_list.push({type, icon, node});
	}

	public static moveProp(type: 'stash' | 'revoke' | 'shuffle', target_node: Node){
		let list = this.prop_list.filter(el=>el.type==type);

		if(list.length <= 0) return Promise.resolve(0);

		let target_pos = Float.ins.uitrans.convertToNodeSpaceAR(target_node.worldPosition);
		let temp_pos = v3(0, 50, 0);

		let count = list.length;
		let np = new mtec.NudityPromise<number>();
		list.forEach((el, i, ls)=>{
			mtec.array.remove(Float.prop_list, el);
			//let [ui_n, ui_t] = [el.node, target_node].map(n=>n.getComponent(UITransform));

			//let scale = Math.min(ui_t.width / ui_n.width, ui_t.height / ui_n.height);

			tween(el.node)
			.by(0.2, {position: temp_pos}, {easing: 'cubicOut'})
			//.to(0.5, {position: target_pos, scale: v3(scale, scale, el.node.scale.z)}, {easing: 'cubicOut'})
			.to(0.5, {position: target_pos}, {easing: 'cubicOut'})
			.call(()=>{
				count--;
				if(count <= 0) np.resolve(ls.length);

				let pool = this.prop_pool.get(el.icon);
				if(!pool){
					pool = new NodePool();
					this.prop_pool.set(el.icon, pool);
				}
				pool.put(el.node);
				el.node.setScale(1, 1, el.node.scale.z);
			}).start();
		});

		return np.promise;
	}

	private static hilight_token: Map<string, {depth: number[], node: Node, parent: Node}[]> = new Map();

	public static heightLight(list: Node[]){
		let canvas = mtec.cc.canvas;
		let base = this.ins.node.children.length;

		let info = list.filter(n=>n && n.isValid).map(node=>{
			let depth: number[] = [];
			let r = node;
			while(r!=canvas.node){
				depth.unshift(r.getSiblingIndex());
				r = r.parent;
			}
			return {depth, node, parent: node.parent};
		}).sort((a, b)=>{
			let i = 0;
			while(a.depth[i]==b.depth[i]) i++;
			return a.depth[i] - b.depth[i];
		});

		info.forEach((el, i)=>{
			el.node.setParent(Float.ins.node, true);
			el.node.setSiblingIndex(base + i);
		});

		let token = mtec.string.randomToken(5, 36, t=>!Float.hilight_token.has(t));

		Float.hilight_token.set(token, info);

		return token;
	}

	public static releaseHILIT(token: string){
		if(!this.hilight_token.has(token)) return void 0;

		let info = this.hilight_token.get(token);
		this.hilight_token.delete(token);

		info.forEach(el=>{
			el.node.setParent(el.parent, true);
			el.node.setSiblingIndex(el.depth.pop());
		});
	}

	private static pool_map: Map<Node, NodePool> = new Map();
	public static Reap(node: Node, count: number, world_position: Vec3, every_call?: (node: Node, index)=>any){
		let target_position = mtec.cc.getLocalPosition(Float.ins.node, node);
		let origin_position = mtec.cc.getLocalPosition(Float.ins.node, world_position);

		let pool = this.pool_map.get(node);
		if(!pool){
			pool = new NodePool();
			this.pool_map.set(node, pool);
		}

		count = count ?? mtec.number.random(3, 5, 0);
		let range = v2(200, 100);
		let np = new mtec.NudityPromise<'end'>();

		let speed = 100 / 0.03;
		let temp_distance = v3();
		for(let i = 0; i<count; i++){
			let item: Node
			if(pool.size() > 0) item = pool.get();
			else item = instantiate(node);

			item.setScale(0.3, 0.3, 1);
			item.setPosition(origin_position);
			item.setParent(Float.ins.node);

			let stop_position = v3(item.position.x + mtec.number.random(-range.x, range.x, 2), item.position.y + mtec.number.random(-range.y, range.y, 2), item.position.z);

			tween(item)
			.to(Vec3.subtract(temp_distance, stop_position, origin_position).length() / speed, {position: stop_position, scale: v3(0.8, 0.8, 1)}, {easing: 'cubicOut'})
			.delay(0.2 + i * 0.15)
			.to(Vec3.subtract(temp_distance, target_position, stop_position).length() / speed, {position: target_position, scale: v3(1, 1, 1)}, {easing: 'cubicIn'})
			.to(0.05, {scale: v3(1.1, 1.1, 1)})
			.to(0.05, {scale: v3(1, 1, 1)})
			.call(()=>{
				if(every_call) every_call(item, i);
				pool.put(item);
				if(i==count-1) np.resolve('end');
			}).start();
		}
		return np.promise;
	}

	public static XX(world_position: Vec3, scale: number){
		scale = scale ?? 1;

		let sp_xx = (Float.ins.xx_pool.get() ?? instantiate(Float.ins.xx_demo)).getComponent(sp.Skeleton);

		sp_xx.node.setParent(Float.ins.node);
		sp_xx.loop = false;
		sp_xx.node.setScale(scale, scale, sp_xx.node.scale.z);
		sp_xx.node.setPosition(Float.ins.uitrans.convertToNodeSpaceAR(world_position));

		sp_xx.setTrackCompleteListener(sp_xx.setAnimation(0, 'xingxing', false), ()=>{
			sp_xx.clearAnimations();
			sp_xx.clearTracks();
			Float.ins.xx_pool.put(sp_xx.node);
		});
	}
}


